/*

DSE Teller System

The system is meant to be used to count the amount of sodas and beers purchased
The system is a replacement for the good old pen and paper system which has been used for decades.
From the ready menu, you only option is to swipe the card in front of the RFID or to hold down the # key.
Holding down # leads you to the maintanance menu.

After swiping the card the user is verified and from here the user can make purchases
Typing 1-4 selects a product
Typing # cancels
Typing 0 gives you status, which also adheres gives you your current balance. 


*/ 
#include <SPI.h>
#include <Ethernet.h>
#include <LiquidCrystal.h>
#include <Keypad.h>
#include <SD.h>

//// INITIALIZE LCD DISPAY
LiquidCrystal lcd(22, 24, 26, 28, 30,32);

/// REID read command
byte MF_GET_SNR[8] = { 0xAA, 0x00, 0x03, 0x25, 0x26, 0x00, 0x00, 0xBB };


/*#####################################################*/
/*################## INITIALIZE ETHERNET ###############*/
/*#####################################################*/


 byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x08, 0x4C };
 IPAddress server(10,250,64,9);

 EthernetClient client;



/*###############################################*/
/*############# INITIALIZE KEYPAD ###############*/
/*###############################################*/

const byte ROWS = 4; // Four rows
const byte COLS = 3; // Three columns

char keys[ROWS][COLS] = {
  {  '1','2','3'  },
  {  '4','5','6'  },
  {  '7','8','9'  },
  {  '*','0','#'  }
};

// Connect keypad ROW0, ROW1, ROW2 and ROW3 to these Arduino pins.
byte rowPins[ROWS] = { 
  36, 46, 44, 40};
// Connect keypad COL0, COL1 and COL2 to these Arduino pins.
byte colPins[COLS] = { 
  38, 34, 42}; 

// Create the Keypad
Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

/*#####################################################*/
/*############## DONE INITIALIZE KEYPAD ###############*/
/*#####################################################*/



/*#####################################################*/
/*################## INITIALIZE USER DB ###############*/
/*#####################################################*/

  int c_beer;
  int c_soda;
  int c_swim;
  int c_swimmotion;


boolean saved = true;
/// 
long current_card = 0;

char bufusercard[19];

char current_card_name[9] = "00000000";
/*#####################################################*/
/*############# DONE INIT USER DB #####################*/
/*#####################################################*/




/*#####################################################*/
/*###################### STRINGS ######################*/
/*#####################################################*/
const char* PLACE_CARD_STRING = "Placer kort";
const char* INITIALIZE_STRING = "Systemet starter op";
const char* DO_PURCHASE_STRING = "Tast dit produkt";
const char* MORE_ABOUT_PURCHASE_STRING = "Se liste";
const char* CARD_READ_STRING = "Kort fundet";
const char* FAILED_TO_RECOGNIZE_USER_STRING = "Ugyldigt kort";
const char* FINDING_USER_STRING = "";
const char* UNIDENTIFIED_PURCHASE = "Ugyldigt produkt";
const char* EMPTY_STRING = "";
const char* BEER_BOUGHT_STRING = "Ol kobt";
const char* SODA_BOUGHT_STRING = "Soda kobt";
const char* SWIM_BOUGHT_STRING = "Svøm kobt";
const char* SWIMMOTION_BOUGHT_STRING = "Svom+motion koebt";
const char* PURCHASE_SAVED = "Gemt!";
const char* NOT_SAVED_STRING = "Du har ikke gemt";
const char* HOW_TO_SAVE_STRING = "Gem med *";
const char* SYNCING_STRING = "Syncing";
/*#####################################################*/
/*#################### DONE STRINGS ###################*/
/*#####################################################*/




/*#####################################################*/
/*###################### MODES ########################*/
/*#####################################################*/
const int READY = 1;
const int CARD_RECEIVED = 2;
const int CARD_VERIFIED = 3;
const int MAINTANANCE = 4;
const int LIST_AVAILABLE_USERS = 5;

int mode = READY;

/*#####################################################*/
/*################## DONE MODES ########################*/
/*#####################################################*/



/*#####################################################*/
/*################### PRICES ##########################*/
/*#####################################################*/
int beer_price = 6;
int soda_price = 6;
int swim_price = 28;
int swimmotion_price = 40;
/*#####################################################*/
/*############### End PRICES ##########################*/
/*#####################################################*/




/*#####################################################*/
/*################## DONE INITIALIZE ETHERNET ###############*/
/*#####################################################*/




 int hash_times;

String usersfldr = "/TELLER/USERS/";
String cardsfldr = "/TELLER/CARDS/";

void setup()
{
  lcd.begin(16,2);
  setDisplay(INITIALIZE_STRING, EMPTY_STRING);
 
  Serial.begin(9600);

  
  
   Serial.println("Configuring Ethernet using DHCP");
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    
    // no point in carrying on, so do nothing forevermore:
    for(;;)
      ;
  }
  
  
  Serial.println("Ethernet Started, delay for a while");
  delay(1000);
  
  
   Serial1.begin(9600);
  Serial1.write(MF_GET_SNR, 8);
  delay(500);
  while(Serial1.available()>0)
  {
    Serial1.read();

  }
  Serial.flush();
  Serial1.flush();
  Serial.println();
  Serial.println("RFID module started in Auto Read Mode");
  
  
  
  
  
  
  
  
  
  
  
  SD.begin();
  delay(200);
  if(!SD.exists("/TELLER")){
    delay(100);
    Serial.println("/TELLER Did not exist, creating it");
    SD.mkdir("/TELLER/");
    delay(100);
  }
  if(!SD.exists("/TELLER/USERS")){
    delay(100);
    Serial.println("/TELLER/USERS Did not exist, creating it");
    SD.mkdir("/TELLER/USERS");
    delay(100);
  }
  if(!SD.exists("/TELLER/CARDS")){
    delay(100);
    Serial.println("/TELLER/CARDS Did not exist, creating it");
     SD.mkdir("/TELLER/CARDS");
     delay(100);
  } 
  Serial.println("Done loading");
  delay(300);
  loadInits();
  delay(300);
    
}
 



void loop()
{
 
  switch(mode){
  case READY:
    read_card(CARD_RECEIVED);
    break;
  case CARD_RECEIVED:
    verify_card_owner();
    break;
  case CARD_VERIFIED:
    handle_purchase();
    break;
   case MAINTANANCE:
    maintanance();
    break;
   case LIST_AVAILABLE_USERS:
    list_available_users();
    break;
  }
  
  char key = kpd.getKey();
  if(key == '*'){
    hash_times = 0;
    cancel();
    mode = MAINTANANCE;
  }

  delay(10);
  
}

/**
 * Verifiyes the card owner by a db lookup
 * # cancels
 */
void verify_card_owner(){
    Serial.println("Verifying card owner");
     c_beer = 0;
     c_soda = 0;
     c_swim = 0;
     c_swimmotion = 0;
     char buf[23];
     (cardsfldr + String(current_card, HEX)).toCharArray(buf,22 );
     if(SD.exists(buf)){
     
         Serial.println(buf);
         File snrfile = SD.open(buf,FILE_READ);
        
         
         char a = snrfile.read();
         
         char username[4] = "NNN";
         int usercount = 0;
          while (a != -1) {
          
            username[usercount] = a;
            usercount++; 
            if(usercount > 3){
              continue; 
            }
            a = snrfile.read();
           }
           
          snrfile.close(); 
         
         
      
         (usersfldr + String(username)).toCharArray(bufusercard,18 );
        
         File current_user_file = SD.open(bufusercard,FILE_READ);
         Serial.println(bufusercard);
     
         char temp[5] = "0000";
         int item_count = 0;
         int char_count = 3; 
         int list[4];
         Serial.println("Reading file");
         char b = current_user_file.read();
         while (b != -1) {
           Serial.print(b);
           
           
           if(b == ';'){
             Serial.println();
             Serial.print("   #");
             Serial.print(temp);
             Serial.print("#  ");
             list[item_count] = atoi(temp);
             item_count ++;
             temp[0] = '0';
             temp[1] = '0';
             temp[2] = '0';
             temp[3] = '0';
             char_count = 4; // 4, not 3 because it gets counted dow
           } else {
             temp[char_count] = b;
           }
           b = current_user_file.read();
           if(item_count > 3){
              current_user_file.close();
              continue; 
           }
           char_count--;
          
         }
          c_beer = (list[0]);
         c_soda = (list[1]);
         c_swim = (list[2]);
         c_swimmotion = (list[3]);
         Serial.println(list[0]);
    
         
          Serial.println("Done Reading file");
          mode = CARD_VERIFIED;
     } else {
       setDisplay( FAILED_TO_RECOGNIZE_USER_STRING, EMPTY_STRING);
       Serial.println("Failed");
     }

  char key = kpd.getKey();
  if(key){
    if(key == '#'){
      cancel();
    }
  }
  
  delay(10);
}


void save(){
  
  File current_user_file = SD.open(bufusercard, FILE_WRITE);
  current_user_file.seek(0);
  current_user_file.print(c_beer, DEC);
  current_user_file.write(";");
  current_user_file.print(c_soda, DEC);
  current_user_file.write(";");
  current_user_file.print(c_swim, DEC);
  current_user_file.write(";");
  current_user_file.print(c_swimmotion,DEC);
  current_user_file.write(";0;0;0;0;0;0;00000000000000000000000000");
  current_user_file.close();
  
  current_user_file = SD.open(bufusercard);
  char b = current_user_file.read();
  Serial.println("Saved:");

  while (b != -1) {
    Serial.print(b);
    b = current_user_file.read();
  }
  
           
}

void cancel()
{
  current_card = 0;
  mode = READY;

}

void handle_purchase(){
  int amount = 1;
  setDisplay(DO_PURCHASE_STRING,MORE_ABOUT_PURCHASE_STRING);
  char key = kpd.getKey();
  if(key){
    switch(key){
    case '0':
      
      view_status();
      
      break;
    case '1':
    c_beer += amount;
    setDisplay(BEER_BOUGHT_STRING, EMPTY_STRING);
    saved = false;
    delay(500);
    break;
  case '2':
    c_soda += amount;
    setDisplay(SODA_BOUGHT_STRING, EMPTY_STRING);
    saved = false;
    delay(500);
    
    break;
  case '3':
    c_swim  += amount;
    setDisplay(SWIM_BOUGHT_STRING, EMPTY_STRING);
    saved = false;
    delay(500);
    break;
  case '4':
    c_swimmotion  += amount;
    setDisplay(SWIMMOTION_BOUGHT_STRING, EMPTY_STRING);
    saved = false;
    delay(500);
    break;
  case '*':
    save();
    setDisplay(PURCHASE_SAVED, EMPTY_STRING);
    saved = true;
    delay(500);
    break;  
    
    case '#':
     if(saved)
     {
       cancel();
     } else {
       setDisplay(NOT_SAVED_STRING, HOW_TO_SAVE_STRING);
       saved = true;
       delay(1000);
     }
      
      break;
    default:
     setDisplay(UNIDENTIFIED_PURCHASE, EMPTY_STRING);
    delay(500);
    }

  }
}

void view_status()
{
  long int balance = c_beer * beer_price + 
                c_soda * soda_price +
                c_swim * swim_price +
                c_swimmotion * swimmotion_price; 
  lcd.clear();
  lcd.write("Balance is:");
  lcd.setCursor(0,1);
  Serial.write(balance);
  lcd.print(balance,DEC);
 
}
/*
void do_purchase(char key, int amount ){
  switch(key){
  case '1':
    current_user.beer += amount;
    setDisplay(BEER_BOUGHT_STRING, EMPTY_STRING);
    delay(500);
    break;
  case '2':
    current_user.soda += amount;
    setDisplay(BEER_BOUGHT_STRING, EMPTY_STRING);
    delay(500);
    break;
  case '3':
    current_user.swim  += amount;
    setDisplay(BEER_BOUGHT_STRING, EMPTY_STRING);
    delay(500);
    break;
  case '4':
    current_user.swimmotion  += amount;
    setDisplay(BEER_BOUGHT_STRING, EMPTY_STRING);
    delay(500);
    
    break;
  default:
    setDisplay(UNIDENTIFIED_PURCHASE, EMPTY_STRING);
    delay(500);
  }
}

*/
/**
 * Reads the card and puts it into to current_card
 * Is run in READY mode
 */
void read_card(const int RETURN_MODE){
  setDisplay(PLACE_CARD_STRING, EMPTY_STRING);
  static bool cardfound;

  /// Send read command to rfid
  Serial1.write(MF_GET_SNR, 8);

  /// Wait for response
  delay(50);

  // i marks where in the input from rfid we are. 
  int i = 0;

  // Contains the input value
  byte temp_input = 0;

  /// Temporary card number
  byte cardno[4];

  /// While rfid has some data in the serial buffer
  /// This loop cannot be broken, it will just crash and burn everything, since the buffer will not be cleared
  while(Serial1.available() > 0) {
    // Get the input byte
    temp_input = Serial1.read();

    

    if(i == 2)
    {
      if(temp_input == 6){ 
        // If second character is 6 then we have a card
        cardfound = true;
      } 
      else {
        cardfound = false;
      }
    }

    if(cardfound && i == 4 && temp_input == 0x01){ 
      // If we have a card and the 4rd caracter is 1, we have two cards
      cardfound = false;
    }
    
    if(cardfound && i == 3){
      /// If we are at the 3rd character, clear display.
      /// Set display might have made this unnecessary
      lcd.clear(); 
    }
    
    if(cardfound && i >= 5 && i < 9){
      /// If we are at the 5th till the 8th character, then it is a part of the card number
      cardno[i - 5] = temp_input;
    }
    
    
    if(cardfound && i == 10){
      /// If we made it till the 10th we are good. 
      /// Using bit masking and shifting we transform the byte array to unsigned long int (32 bit)
      unsigned long value = 0;
      for (int k = 0; k < 4; k++) {
        int shift = (3 - k) * 8;
        unsigned long mask = 0x000000FF;
        value += (cardno[k] & mask) << shift;
      }
      
      /// Save the card and swithc mode
      current_card = value;
      mode = RETURN_MODE;//;
    }
    i++;
  }



}

  

void maintanance()
{
  static const char* IN_MAINTANANCE = "Maintanance";
  setDisplay(IN_MAINTANANCE, EMPTY_STRING);
  char key = kpd.getKey();
  if(key){
    switch(key)
    {
      Serial.print("Pressed ");
      Serial.println(key);
      case '1':
        loadInits();
        break;
      case '5':
        sync();
        break;
         
     case '9':
        list_available_users();
        break;
      case '#':
        cancel();  
        break;
    }
  }
}


void sync()
{
  setDisplay(SYNCING_STRING, EMPTY_STRING);
  delay(1000);
 
  Serial.println("connecting...");

  if (client.connect(server, 80)) {
    Serial.println("connected");
    client.println("GET http://kaps.studerende.dk/in.php?data=arduino HTTP/1.0");
    client.println();
  } 
  else {
    // kf you didn't get a connection to the server:
    Serial.println("connection failed");
  }
 
  while (client.available()) {
    char c = client.read();
    Serial.print(c);
  

 
    if (!client.connected()) {
      Serial.println();
      Serial.println("disconnecting.");
      client.stop();
    }
  }
  
  

  
}


void list_available_users(){
  
  setDisplay(EMPTY_STRING,EMPTY_STRING);
  File dir = SD.open("/TELLER/USERS/");
  printDirectory(dir, 0);
   dir = SD.open("/TELLER/USERS/");
  
  File entry =  dir.openNextFile();
  while(entry){
    lcd.clear();
    lcd.print(entry.name());
    char key = 0;
    while(!key){
      key = kpd.getKey();
      delay(10);
    } 
    if(key == '#') { 
      entry.close();
      dir.close();
      cancel(); 
      break;
    } 
     if(key == '7'){
       setDisplay(PLACE_CARD_STRING, EMPTY_STRING);
       while(current_card == 0){
         read_card(MAINTANANCE);  
       }
        char buf[23];
        (cardsfldr + String(current_card, HEX)).toCharArray(buf,22 );
        if(SD.exists(buf)){
          
           lcd.clear();
           lcd.write("EXISTED");
            entry.close();
            dir.close();
            cancel(); 
            break;
        } else {
           lcd.clear();
           lcd.write("Got card");
           File snrfile = SD.open(buf,FILE_WRITE);
           snrfile.write(entry.name());
           snrfile.close();
           delay(1000);
           cancel(); 
            break;
        }
         Serial.println(buf);
        delay(10000);
        cancel();
     }
     entry =  dir.openNextFile();
     Serial.println("openiinge newty");
     delay(10);
  }
  
}

void printDirectory(File dir, int numTabs) {
   while(true) {

     File entry =  dir.openNextFile();
     if (! entry) {
       // no more files
       Serial.println("**nomorefiles**");
       break;
     }
     for (uint8_t i=0; i<numTabs; i++) {
       Serial.print('\t');
     }
     Serial.print(entry.name());
     if (entry.isDirectory()) {
       Serial.println("/");
       printDirectory(entry, numTabs+1);
     } else {
       // files have sizes, directories do not
       Serial.print("\t\t");
       Serial.println(entry.size(), DEC);
     }
   }
}

void loadInits(){
  char list[3][4] = { "RVP", "SLP", "CSN" };
  for(int i = 0; i < 3; i++)
  { 
    char buf[18];
    (usersfldr + list[i]).toCharArray(buf,18 );
    if(!SD.exists(buf))
    {
     
      File file = SD.open(buf, FILE_WRITE);
      file.seek(0);
      file.write("0;0;0;0;0;0;0;0;0;0;0;0;0;0;0");
      file.close();
    }
  }
}

void setDisplay(const char * row1, const char * row2){
  static const char* lastrow1;
  static const char* lastrow2;
  if(row1 != lastrow1 || row2 != lastrow2)
  {
    lcd.clear();
    lcd.print(row1);
   
    lcd.setCursor(0,1);
    lcd.print(row2);
    
    lastrow1 = row1;
    lastrow2 = row2;
  }

}






















